<?php

// 命名空间
namespace Xzb\Ci3\Database\Eloquent\Traits;

use Xzb\Ci3\Database\Eloquent\Relations\{
	HasOne,
	HasMany,
	BelongsTo,
	BelongsToMany,
	hasOneThrough,
	hasManyThrough
};

use Xzb\Ci3\Database\Eloquent\Model;

/**
 * 关系
 */
trait HasRelationships
{
	/**
	 * 一对一
	 * 
	 * @param string $related
	 * @param string $foreignKey
	 * @param string $primaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\HasOne
	 */
	public function hasOne(string $related, $foreignKey = null, $primaryKey = null)
	{
		$relatedInstance = new $related;

		$foreignKey = $foreignKey ?: $this->getForeignKeyName();
		$primaryKey = $primaryKey ?: $this->getPrimaryKeyName();

		return new HasOne($this, $relatedInstance, $foreignKey, $primaryKey);
	}

	/**
	 * 一对多
	 * 
	 * @param string $related
	 * @param string $foreignKey
	 * @param string $primaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\HasMany
	 */
	public function hasMany(string $related, $foreignKey = null, $primaryKey = null)
	{
		$relatedInstance = new $related;

		$foreignKey = $foreignKey ?: $this->getForeignKeyName();
		$primaryKey = $primaryKey ?: $this->getPrimaryKeyName();

		return new HasMany($this, $relatedInstance, $foreignKey, $primaryKey);
	}

	/**
	 * 属于
	 * 
	 * @param string $related
	 * @param string $foreignKey
	 * @param string $primaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\BelongsTo
	 */
	public function belongsTo(
		string $related,
		string $foreignKey = null, string $primaryKey = null,
		string $relationName = null
	)
	{
		$relatedInstance = new $related;

		$foreignKey = $foreignKey ?: $relatedInstance->getForeignKeyName();
		$primaryKey = $primaryKey ?: $relatedInstance->getPrimaryKeyName();

		$relationName = $relationName ?: $this->guessRelationName();

		return new BelongsTo($this, $relatedInstance, $foreignKey, $primaryKey, $relationName);
	}

	/**
	 * 多对多
	 * 
	 * @param string $related
	 * @param string $table
	 * @param string $parentForeignKey
	 * @param string $relatedForeignKey
	 * @param string $parentPrimaryKey
	 * @param string $relatedPrimaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\BelongsToMany
	 */
	public function belongsToMany(
		string $related, string $table = null,
		string $parentForeignKey = null, string $relatedForeignKey = null,
		string $parentPrimaryKey = null, string $relatedPrimaryKey = null
	)
	{
		$relatedInstance = new $related;

		$table = $table ?: $this->joiningTable($relatedInstance);

		$parentForeignKey = $parentForeignKey ?: $this->getForeignKeyName();
		$relatedForeignKey = $relatedForeignKey ?: $relatedInstance->getForeignKeyName();

		$parentPrimaryKey = $parentPrimaryKey ?: $this->getPrimaryKeyName();
		$relatedPrimaryKey = $relatedPrimaryKey ?: $relatedInstance->getPrimaryKeyName();

		return new BelongsToMany(
			$this, $relatedInstance, $table,
			$parentForeignKey, $relatedForeignKey,
			$parentPrimaryKey, $relatedPrimaryKey
		);
	}


	/**
	 * 一对一 远程
	 * 
	 * @param string $related
	 * @param string $through
	 * @param string $parentForeignKey
	 * @param string $throughForeignKey
	 * @param string $parentPrimaryKey
	 * @param string $throughPrimaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\hasOneThrough
	 */
	public function hasOneThrough(
		string $related, string $through,
		string $parentForeignKey = null, string $throughForeignKey = null,
		string $parentPrimaryKey = null, string $throughPrimaryKey = null
	)
	{
		$relatedInstance = new $related;
		$throughInstance = new $through;

		$parentForeignKey = $parentForeignKey ?: $this->getForeignKeyName();
		$throughForeignKey = $throughForeignKey ?: $throughInstance->getForeignKeyName();

		$parentPrimaryKey = $parentPrimaryKey ?: $this->getPrimaryKeyName();
		$throughPrimaryKey = $throughPrimaryKey ?: $throughInstance->getPrimaryKeyName();

		return new hasOneThrough(
			$this, $relatedInstance, $throughInstance,
			$parentForeignKey, $throughForeignKey,
			$parentPrimaryKey, $throughPrimaryKey
		);
	}

	/**
	 * 一对多 远程
	 * 
	 * @param string $related
	 * @param string $through
	 * @param string $parentForeignKey
	 * @param string $throughForeignKey
	 * @param string $parentPrimaryKey
	 * @param string $throughPrimaryKey
	 * @return \Xzb\Ci3\Database\Eloquent\Relations\hasManyThrough
	 */
	public function hasManyThrough(
		string $related, string $through,
		string $parentForeignKey = null, string $throughForeignKey = null,
		string $parentPrimaryKey = null, string $throughPrimaryKey = null
	)
	{
		$relatedInstance = new $related;
		$throughInstance = new $through;

		$parentForeignKey = $parentForeignKey ?: $this->getForeignKeyName();
		$throughForeignKey = $throughForeignKey ?: $throughInstance->getForeignKeyName();

		$parentPrimaryKey = $parentPrimaryKey ?: $this->getPrimaryKeyName();
		$throughPrimaryKey = $throughPrimaryKey ?: $throughInstance->getPrimaryKeyName();

		return new HasManyThrough(
			$this, $relatedInstance, $throughInstance,
			$parentForeignKey, $throughForeignKey,
			$parentPrimaryKey, $throughPrimaryKey
		);
	}

	/**
	 * 获取 关系名称
	 * 
	 * @return string
	 */
	protected function guessRelationName()
	{
		[$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);

		return $caller['function'];
	}

	/**
	 * 获取 多对多 关系的 中间表名
	 * 
	 * @param \Xzb\Ci3\Database\Eloquent\Model $relatedInstance 
	 * @return string
	 */
	protected function joiningTable(Model $relatedInstance)
	{
		$segments = [
			$relatedInstance->getJoiningTableSegment(),
			$this->getJoiningTableSegment(),
		];

		sort($segments);

		return strtolower(implode('_', $segments));
	}
}

// namespace Illuminate\Database\Eloquent\Concerns;

// use Closure;
// use Illuminate\Database\ClassMorphViolationException;
// use Illuminate\Database\Eloquent\Builder;
// use Illuminate\Database\Eloquent\Collection;
// use Illuminate\Database\Eloquent\Model;
// use Illuminate\Database\Eloquent\PendingHasThroughRelationship;
// use Illuminate\Database\Eloquent\Relations\BelongsTo;
// use Illuminate\Database\Eloquent\Relations\BelongsToMany;
// use Illuminate\Database\Eloquent\Relations\HasMany;
// use Illuminate\Database\Eloquent\Relations\HasManyThrough;
// use Illuminate\Database\Eloquent\Relations\HasOne;
// use Illuminate\Database\Eloquent\Relations\HasOneThrough;
// use Illuminate\Database\Eloquent\Relations\MorphMany;
// use Illuminate\Database\Eloquent\Relations\MorphOne;
// use Illuminate\Database\Eloquent\Relations\MorphTo;
// use Illuminate\Database\Eloquent\Relations\MorphToMany;
// use Illuminate\Database\Eloquent\Relations\Pivot;
// use Illuminate\Database\Eloquent\Relations\Relation;
// use Illuminate\Support\Arr;
// use Illuminate\Support\Str;

// trait HasRelationships
// {
//     /**
//      * The loaded relationships for the model.
//      *
//      * @var array
//      */
//     protected $relations = [];

//     /**
//      * The relationships that should be touched on save.
//      *
//      * @var array
//      */
//     protected $touches = [];

//     /**
//      * The many to many relationship methods.
//      *
//      * @var string[]
//      */
//     public static $manyMethods = [
//         'belongsToMany', 'morphToMany', 'morphedByMany',
//     ];

//     /**
//      * The relation resolver callbacks.
//      *
//      * @var array
//      */
//     protected static $relationResolvers = [];

//     /**
//      * Get the dynamic relation resolver if defined or inherited, or return null.
//      *
//      * @param  string  $class
//      * @param  string  $key
//      * @return mixed
//      */
//     public function relationResolver($class, $key)
//     {
//         if ($resolver = static::$relationResolvers[$class][$key] ?? null) {
//             return $resolver;
//         }

//         if ($parent = get_parent_class($class)) {
//             return $this->relationResolver($parent, $key);
//         }

//         return null;
//     }

//     /**
//      * Define a dynamic relation resolver.
//      *
//      * @param  string  $name
//      * @param  \Closure  $callback
//      * @return void
//      */
//     public static function resolveRelationUsing($name, Closure $callback)
//     {
//         static::$relationResolvers = array_replace_recursive(
//             static::$relationResolvers,
//             [static::class => [$name => $callback]]
//         );
//     }

//     /**
//      * Define a one-to-one relationship.
//      *
//      * @param  string  $related
//      * @param  string|null  $foreignKey
//      * @param  string|null  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasOne
//      */
//     public function hasOne($related, $foreignKey = null, $localKey = null)
//     {
//         $instance = $this->newRelatedInstance($related);

//         $foreignKey = $foreignKey ?: $this->getForeignKey();

//         $localKey = $localKey ?: $this->getKeyName();

//         return $this->newHasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
//     }

//     /**
//      * Instantiate a new HasOne relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $foreignKey
//      * @param  string  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasOne
//      */
//     protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
//     {
//         return new HasOne($query, $parent, $foreignKey, $localKey);
//     }

//     /**
//      * Define a has-one-through relationship.
//      *
//      * @param  string  $related
//      * @param  string  $through
//      * @param  string|null  $firstKey
//      * @param  string|null  $secondKey
//      * @param  string|null  $localKey
//      * @param  string|null  $secondLocalKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
//      */
//     public function hasOneThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
//     {
//         $through = $this->newRelatedThroughInstance($through);

//         $firstKey = $firstKey ?: $this->getForeignKey();

//         $secondKey = $secondKey ?: $through->getForeignKey();

//         return $this->newHasOneThrough(
//             $this->newRelatedInstance($related)->newQuery(), $this, $through,
//             $firstKey, $secondKey, $localKey ?: $this->getKeyName(),
//             $secondLocalKey ?: $through->getKeyName()
//         );
//     }

//     /**
//      * Instantiate a new HasOneThrough relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $farParent
//      * @param  \Illuminate\Database\Eloquent\Model  $throughParent
//      * @param  string  $firstKey
//      * @param  string  $secondKey
//      * @param  string  $localKey
//      * @param  string  $secondLocalKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
//      */
//     protected function newHasOneThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
//     {
//         return new HasOneThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
//     }

//     /**
//      * Define a polymorphic one-to-one relationship.
//      *
//      * @param  string  $related
//      * @param  string  $name
//      * @param  string|null  $type
//      * @param  string|null  $id
//      * @param  string|null  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphOne
//      */
//     public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
//     {
//         $instance = $this->newRelatedInstance($related);

//         [$type, $id] = $this->getMorphs($name, $type, $id);

//         $table = $instance->getTable();

//         $localKey = $localKey ?: $this->getKeyName();

//         return $this->newMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
//     }

//     /**
//      * Instantiate a new MorphOne relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $type
//      * @param  string  $id
//      * @param  string  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphOne
//      */
//     protected function newMorphOne(Builder $query, Model $parent, $type, $id, $localKey)
//     {
//         return new MorphOne($query, $parent, $type, $id, $localKey);
//     }

//     /**
//      * Define an inverse one-to-one or many relationship.
//      *
//      * @param  string  $related
//      * @param  string|null  $foreignKey
//      * @param  string|null  $ownerKey
//      * @param  string|null  $relation
//      * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
//      */
//     public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
//     {
//         // If no relation name was given, we will use this debug backtrace to extract
//         // the calling method's name and use that as the relationship name as most
//         // of the time this will be what we desire to use for the relationships.
//         if (is_null($relation)) {
//             $relation = $this->guessBelongsToRelation();
//         }

//         $instance = $this->newRelatedInstance($related);

//         // If no foreign key was supplied, we can use a backtrace to guess the proper
//         // foreign key name by using the name of the relationship function, which
//         // when combined with an "_id" should conventionally match the columns.
//         if (is_null($foreignKey)) {
//             $foreignKey = Str::snake($relation).'_'.$instance->getKeyName();
//         }

//         // Once we have the foreign key names we'll just create a new Eloquent query
//         // for the related models and return the relationship instance which will
//         // actually be responsible for retrieving and hydrating every relation.
//         $ownerKey = $ownerKey ?: $instance->getKeyName();

//         return $this->newBelongsTo(
//             $instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
//         );
//     }

//     /**
//      * Instantiate a new BelongsTo relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $child
//      * @param  string  $foreignKey
//      * @param  string  $ownerKey
//      * @param  string  $relation
//      * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
//      */
//     protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
//     {
//         return new BelongsTo($query, $child, $foreignKey, $ownerKey, $relation);
//     }

//     /**
//      * Define a polymorphic, inverse one-to-one or many relationship.
//      *
//      * @param  string|null  $name
//      * @param  string|null  $type
//      * @param  string|null  $id
//      * @param  string|null  $ownerKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphTo
//      */
//     public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
//     {
//         // If no name is provided, we will use the backtrace to get the function name
//         // since that is most likely the name of the polymorphic interface. We can
//         // use that to get both the class and foreign key that will be utilized.
//         $name = $name ?: $this->guessBelongsToRelation();

//         [$type, $id] = $this->getMorphs(
//             Str::snake($name), $type, $id
//         );

//         // If the type value is null it is probably safe to assume we're eager loading
//         // the relationship. In this case we'll just pass in a dummy query where we
//         // need to remove any eager loads that may already be defined on a model.
//         return is_null($class = $this->getAttributeFromArray($type)) || $class === ''
//                     ? $this->morphEagerTo($name, $type, $id, $ownerKey)
//                     : $this->morphInstanceTo($class, $name, $type, $id, $ownerKey);
//     }

//     /**
//      * Define a polymorphic, inverse one-to-one or many relationship.
//      *
//      * @param  string  $name
//      * @param  string  $type
//      * @param  string  $id
//      * @param  string  $ownerKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphTo
//      */
//     protected function morphEagerTo($name, $type, $id, $ownerKey)
//     {
//         return $this->newMorphTo(
//             $this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name
//         );
//     }

//     /**
//      * Define a polymorphic, inverse one-to-one or many relationship.
//      *
//      * @param  string  $target
//      * @param  string  $name
//      * @param  string  $type
//      * @param  string  $id
//      * @param  string  $ownerKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphTo
//      */
//     protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
//     {
//         $instance = $this->newRelatedInstance(
//             static::getActualClassNameForMorph($target)
//         );

//         return $this->newMorphTo(
//             $instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name
//         );
//     }

//     /**
//      * Instantiate a new MorphTo relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $foreignKey
//      * @param  string  $ownerKey
//      * @param  string  $type
//      * @param  string  $relation
//      * @return \Illuminate\Database\Eloquent\Relations\MorphTo
//      */
//     protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
//     {
//         return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
//     }

//     /**
//      * Retrieve the actual class name for a given morph class.
//      *
//      * @param  string  $class
//      * @return string
//      */
//     public static function getActualClassNameForMorph($class)
//     {
//         return Arr::get(Relation::morphMap() ?: [], $class, $class);
//     }

//     /**
//      * Guess the "belongs to" relationship name.
//      *
//      * @return string
//      */
//     protected function guessBelongsToRelation()
//     {
//         [$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);

//         return $caller['function'];
//     }

//     /**
//      * Create a pending has-many-through or has-one-through relationship.
//      *
//      * @param  string|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Eloquent\Relations\HasOne  $relationship
//      * @return \Illuminate\Database\Eloquent\PendingHasThroughRelationship
//      */
//     public function through($relationship)
//     {
//         if (is_string($relationship)) {
//             $relationship = $this->{$relationship}();
//         }

//         return new PendingHasThroughRelationship($this, $relationship);
//     }

//     /**
//      * Define a one-to-many relationship.
//      *
//      * @param  string  $related
//      * @param  string|null  $foreignKey
//      * @param  string|null  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasMany
//      */
//     public function hasMany($related, $foreignKey = null, $localKey = null)
//     {
//         $instance = $this->newRelatedInstance($related);

//         $foreignKey = $foreignKey ?: $this->getForeignKey();

//         $localKey = $localKey ?: $this->getKeyName();

//         return $this->newHasMany(
//             $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
//         );
//     }

//     /**
//      * Instantiate a new HasMany relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $foreignKey
//      * @param  string  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasMany
//      */
//     protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)
//     {
//         return new HasMany($query, $parent, $foreignKey, $localKey);
//     }

//     /**
//      * Define a has-many-through relationship.
//      *
//      * @param  string  $related
//      * @param  string  $through
//      * @param  string|null  $firstKey
//      * @param  string|null  $secondKey
//      * @param  string|null  $localKey
//      * @param  string|null  $secondLocalKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
//      */
//     public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
//     {
//         $through = $this->newRelatedThroughInstance($through);

//         $firstKey = $firstKey ?: $this->getForeignKey();

//         $secondKey = $secondKey ?: $through->getForeignKey();

//         return $this->newHasManyThrough(
//             $this->newRelatedInstance($related)->newQuery(),
//             $this,
//             $through,
//             $firstKey,
//             $secondKey,
//             $localKey ?: $this->getKeyName(),
//             $secondLocalKey ?: $through->getKeyName()
//         );
//     }

//     /**
//      * Instantiate a new HasManyThrough relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $farParent
//      * @param  \Illuminate\Database\Eloquent\Model  $throughParent
//      * @param  string  $firstKey
//      * @param  string  $secondKey
//      * @param  string  $localKey
//      * @param  string  $secondLocalKey
//      * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
//      */
//     protected function newHasManyThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
//     {
//         return new HasManyThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
//     }

//     /**
//      * Define a polymorphic one-to-many relationship.
//      *
//      * @param  string  $related
//      * @param  string  $name
//      * @param  string|null  $type
//      * @param  string|null  $id
//      * @param  string|null  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
//      */
//     public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
//     {
//         $instance = $this->newRelatedInstance($related);

//         // Here we will gather up the morph type and ID for the relationship so that we
//         // can properly query the intermediate table of a relation. Finally, we will
//         // get the table and create the relationship instances for the developers.
//         [$type, $id] = $this->getMorphs($name, $type, $id);

//         $table = $instance->getTable();

//         $localKey = $localKey ?: $this->getKeyName();

//         return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
//     }

//     /**
//      * Instantiate a new MorphMany relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $type
//      * @param  string  $id
//      * @param  string  $localKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
//      */
//     protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
//     {
//         return new MorphMany($query, $parent, $type, $id, $localKey);
//     }

//     /**
//      * Define a many-to-many relationship.
//      *
//      * @param  string  $related
//      * @param  string|null  $table
//      * @param  string|null  $foreignPivotKey
//      * @param  string|null  $relatedPivotKey
//      * @param  string|null  $parentKey
//      * @param  string|null  $relatedKey
//      * @param  string|null  $relation
//      * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
//      */
//     public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
//                                   $parentKey = null, $relatedKey = null, $relation = null)
//     {
//         // If no relationship name was passed, we will pull backtraces to get the
//         // name of the calling function. We will use that function name as the
//         // title of this relation since that is a great convention to apply.
//         if (is_null($relation)) {
//             $relation = $this->guessBelongsToManyRelation();
//         }

//         // First, we'll need to determine the foreign key and "other key" for the
//         // relationship. Once we have determined the keys we'll make the query
//         // instances as well as the relationship instances we need for this.
//         $instance = $this->newRelatedInstance($related);

//         $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();

//         $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();

//         // If no table name was provided, we can guess it by concatenating the two
//         // models using underscores in alphabetical order. The two model names
//         // are transformed to snake case from their default CamelCase also.
//         if (is_null($table)) {
//             $table = $this->joiningTable($related, $instance);
//         }

//         return $this->newBelongsToMany(
//             $instance->newQuery(), $this, $table, $foreignPivotKey,
//             $relatedPivotKey, $parentKey ?: $this->getKeyName(),
//             $relatedKey ?: $instance->getKeyName(), $relation
//         );
//     }

//     /**
//      * Instantiate a new BelongsToMany relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $table
//      * @param  string  $foreignPivotKey
//      * @param  string  $relatedPivotKey
//      * @param  string  $parentKey
//      * @param  string  $relatedKey
//      * @param  string|null  $relationName
//      * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
//      */
//     protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
//                                         $parentKey, $relatedKey, $relationName = null)
//     {
//         return new BelongsToMany($query, $parent, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $relationName);
//     }

//     /**
//      * Define a polymorphic many-to-many relationship.
//      *
//      * @param  string  $related
//      * @param  string  $name
//      * @param  string|null  $table
//      * @param  string|null  $foreignPivotKey
//      * @param  string|null  $relatedPivotKey
//      * @param  string|null  $parentKey
//      * @param  string|null  $relatedKey
//      * @param  bool  $inverse
//      * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
//      */
//     public function morphToMany($related, $name, $table = null, $foreignPivotKey = null,
//                                 $relatedPivotKey = null, $parentKey = null,
//                                 $relatedKey = null, $inverse = false)
//     {
//         $caller = $this->guessBelongsToManyRelation();

//         // First, we will need to determine the foreign key and "other key" for the
//         // relationship. Once we have determined the keys we will make the query
//         // instances, as well as the relationship instances we need for these.
//         $instance = $this->newRelatedInstance($related);

//         $foreignPivotKey = $foreignPivotKey ?: $name.'_id';

//         $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();

//         // Now we're ready to create a new query builder for the related model and
//         // the relationship instances for this relation. This relation will set
//         // appropriate query constraints then entirely manage the hydrations.
//         if (! $table) {
//             $words = preg_split('/(_)/u', $name, -1, PREG_SPLIT_DELIM_CAPTURE);

//             $lastWord = array_pop($words);

//             $table = implode('', $words).Str::plural($lastWord);
//         }

//         return $this->newMorphToMany(
//             $instance->newQuery(), $this, $name, $table,
//             $foreignPivotKey, $relatedPivotKey, $parentKey ?: $this->getKeyName(),
//             $relatedKey ?: $instance->getKeyName(), $caller, $inverse
//         );
//     }

//     /**
//      * Instantiate a new MorphToMany relationship.
//      *
//      * @param  \Illuminate\Database\Eloquent\Builder  $query
//      * @param  \Illuminate\Database\Eloquent\Model  $parent
//      * @param  string  $name
//      * @param  string  $table
//      * @param  string  $foreignPivotKey
//      * @param  string  $relatedPivotKey
//      * @param  string  $parentKey
//      * @param  string  $relatedKey
//      * @param  string|null  $relationName
//      * @param  bool  $inverse
//      * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
//      */
//     protected function newMorphToMany(Builder $query, Model $parent, $name, $table, $foreignPivotKey,
//                                       $relatedPivotKey, $parentKey, $relatedKey,
//                                       $relationName = null, $inverse = false)
//     {
//         return new MorphToMany($query, $parent, $name, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey,
//             $relationName, $inverse);
//     }

//     /**
//      * Define a polymorphic, inverse many-to-many relationship.
//      *
//      * @param  string  $related
//      * @param  string  $name
//      * @param  string|null  $table
//      * @param  string|null  $foreignPivotKey
//      * @param  string|null  $relatedPivotKey
//      * @param  string|null  $parentKey
//      * @param  string|null  $relatedKey
//      * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
//      */
//     public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null,
//                                   $relatedPivotKey = null, $parentKey = null, $relatedKey = null)
//     {
//         $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();

//         // For the inverse of the polymorphic many-to-many relations, we will change
//         // the way we determine the foreign and other keys, as it is the opposite
//         // of the morph-to-many method since we're figuring out these inverses.
//         $relatedPivotKey = $relatedPivotKey ?: $name.'_id';

//         return $this->morphToMany(
//             $related, $name, $table, $foreignPivotKey,
//             $relatedPivotKey, $parentKey, $relatedKey, true
//         );
//     }

//     /**
//      * Get the relationship name of the belongsToMany relationship.
//      *
//      * @return string|null
//      */
//     protected function guessBelongsToManyRelation()
//     {
//         $caller = Arr::first(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), function ($trace) {
//             return ! in_array(
//                 $trace['function'],
//                 array_merge(static::$manyMethods, ['guessBelongsToManyRelation'])
//             );
//         });

//         return ! is_null($caller) ? $caller['function'] : null;
//     }

//     /**
//      * Get the joining table name for a many-to-many relation.
//      *
//      * @param  string  $related
//      * @param  \Illuminate\Database\Eloquent\Model|null  $instance
//      * @return string
//      */
//     public function joiningTable($related, $instance = null)
//     {
//         // The joining table name, by convention, is simply the snake cased models
//         // sorted alphabetically and concatenated with an underscore, so we can
//         // just sort the models and join them together to get the table name.
//         $segments = [
//             $instance ? $instance->joiningTableSegment()
//                       : Str::snake(class_basename($related)),
//             $this->joiningTableSegment(),
//         ];

//         // Now that we have the model names in an array we can just sort them and
//         // use the implode function to join them together with an underscores,
//         // which is typically used by convention within the database system.
//         sort($segments);

//         return strtolower(implode('_', $segments));
//     }

//     /**
//      * Get this model's half of the intermediate table name for belongsToMany relationships.
//      *
//      * @return string
//      */
//     public function joiningTableSegment()
//     {
//         return Str::snake(class_basename($this));
//     }

//     /**
//      * Determine if the model touches a given relation.
//      *
//      * @param  string  $relation
//      * @return bool
//      */
//     public function touches($relation)
//     {
//         return in_array($relation, $this->getTouchedRelations());
//     }

//     /**
//      * Touch the owning relations of the model.
//      *
//      * @return void
//      */
//     public function touchOwners()
//     {
//         foreach ($this->getTouchedRelations() as $relation) {
//             $this->$relation()->touch();

//             if ($this->$relation instanceof self) {
//                 $this->$relation->fireModelEvent('saved', false);

//                 $this->$relation->touchOwners();
//             } elseif ($this->$relation instanceof Collection) {
//                 $this->$relation->each->touchOwners();
//             }
//         }
//     }

//     /**
//      * Get the polymorphic relationship columns.
//      *
//      * @param  string  $name
//      * @param  string  $type
//      * @param  string  $id
//      * @return array
//      */
//     protected function getMorphs($name, $type, $id)
//     {
//         return [$type ?: $name.'_type', $id ?: $name.'_id'];
//     }

//     /**
//      * Get the class name for polymorphic relations.
//      *
//      * @return string
//      */
//     public function getMorphClass()
//     {
//         $morphMap = Relation::morphMap();

//         if (! empty($morphMap) && in_array(static::class, $morphMap)) {
//             return array_search(static::class, $morphMap, true);
//         }

//         if (static::class === Pivot::class) {
//             return static::class;
//         }

//         if (Relation::requiresMorphMap()) {
//             throw new ClassMorphViolationException($this);
//         }

//         return static::class;
//     }

//     /**
//      * Create a new model instance for a related model.
//      *
//      * @param  string  $class
//      * @return mixed
//      */
//     protected function newRelatedInstance($class)
//     {
//         return tap(new $class, function ($instance) {
//             if (! $instance->getConnectionName()) {
//                 $instance->setConnection($this->connection);
//             }
//         });
//     }

//     /**
//      * Create a new model instance for a related "through" model.
//      *
//      * @param  string  $class
//      * @return mixed
//      */
//     protected function newRelatedThroughInstance($class)
//     {
//         return new $class;
//     }

//     /**
//      * Get all the loaded relations for the instance.
//      *
//      * @return array
//      */
//     public function getRelations()
//     {
//         return $this->relations;
//     }

//     /**
//      * Get a specified relationship.
//      *
//      * @param  string  $relation
//      * @return mixed
//      */
//     public function getRelation($relation)
//     {
//         return $this->relations[$relation];
//     }

//     /**
//      * Determine if the given relation is loaded.
//      *
//      * @param  string  $key
//      * @return bool
//      */
//     public function relationLoaded($key)
//     {
//         return array_key_exists($key, $this->relations);
//     }

//     /**
//      * Set the given relationship on the model.
//      *
//      * @param  string  $relation
//      * @param  mixed  $value
//      * @return $this
//      */
//     public function setRelation($relation, $value)
//     {
//         $this->relations[$relation] = $value;

//         return $this;
//     }

//     /**
//      * Unset a loaded relationship.
//      *
//      * @param  string  $relation
//      * @return $this
//      */
//     public function unsetRelation($relation)
//     {
//         unset($this->relations[$relation]);

//         return $this;
//     }

//     /**
//      * Set the entire relations array on the model.
//      *
//      * @param  array  $relations
//      * @return $this
//      */
//     public function setRelations(array $relations)
//     {
//         $this->relations = $relations;

//         return $this;
//     }

//     /**
//      * Duplicate the instance and unset all the loaded relations.
//      *
//      * @return $this
//      */
//     public function withoutRelations()
//     {
//         $model = clone $this;

//         return $model->unsetRelations();
//     }

//     /**
//      * Unset all the loaded relations for the instance.
//      *
//      * @return $this
//      */
//     public function unsetRelations()
//     {
//         $this->relations = [];

//         return $this;
//     }

//     /**
//      * Get the relationships that are touched on save.
//      *
//      * @return array
//      */
//     public function getTouchedRelations()
//     {
//         return $this->touches;
//     }

//     /**
//      * Set the relationships that are touched on save.
//      *
//      * @param  array  $touches
//      * @return $this
//      */
//     public function setTouchedRelations(array $touches)
//     {
//         $this->touches = $touches;

//         return $this;
//     }
// }
